#This file is part of Nereid.  The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
"""
General Utilities
"""
import logging

from werkzeug import Local, LocalManager
from werkzeug import Response
from werkzeug.wrappers import BaseResponse
from werkzeug.routing import Map, Rule
from proteus import config, Model 
from nereid.exceptions import ModelNotFound
from hashcompat import md5_constructor

#Refer http://werkzeug.pocoo.org/documentation/dev/local.html
#for the purpose of locals
#thread/request specific attributes can be stored in local
local = Local()
#Local objects cannot manage themselves. 
#For that you need a local manager. 
#You can pass a local manager multiple locals or add them later by 
#appending them to manager.locals. 
#Everytime the manager cleans up it, will clean up all the data 
#left in the locals for this context.
local_manager = LocalManager([local])

#The password is stored as a salted hash. This character separates
#the randomly generated salt from the password 
PASSWORD_SEPARATOR = '$'

def generate_url_map():
    """
    Creates an URL map from URLS constructed in given database
    The map is a dictionary where key is the site domain
    and value is a Map object with all rules
    
    The url_map is a dictionary and stores the URLs against the domain
    names registered in sites
    """
    Site = Model.get('nereid.site')
    URL = Model.get('nereid.url')
    url_map = {}
    
    sites = Site.find([('active', '=', True)])
    for site in sites:
        #Rules for the specific site
        rules = [
             Rule(
                string=url.route_path,
                defaults=url.defaults and eval(url.defaults) or {},
                subdomain=None,
                methods=None,
                build_only=False,
                endpoint='%s/%s' % (url.model.model, url.callable),
                #The endpoint is recorded in the format
                #`nereid.url/redirect`
                #`<model>/<callable>`
                strict_slashes=None,
                redirect_to=(url.url_type == 'redirect') \
                            and eval(url.defaults).get('url', None) or None
                ) for url in URL.find([('site', '=', site.id)])]
        url_map[site.domain] = Map(rules)
    return url_map

def index(request, *args):
    """
    A Sample Hello World response for testing
    For example you might want to have a method
    in your model which will be coded later but just want to test
    
    In that case just use 
        `from nereid import index
        ....
        #In the method use
        return index(request)`  
    """
    return Response("""
    <html>
        <body>
        Hello world <br/>Request: %s<br/>Args: %s
        </body>
    </html>
    """ % (
           request,
           args),
    mimetype="text/html"
    )

def _get_random_salt():
    """
    
    """
    
def make_salted_hash(raw_password):
    """
    Creates a Salted Hash password like Django
    @param password:Plain text password 
    @return: Salted hash of given password
    """
    salt = _get_random_salt()
    password = md5_constructor(salt + raw_password).hexdigest()
    return PASSWORD_SEPARATOR.join(salt, password)

class CallableProxyHandler(object):
    """
    All responses from a WSGI app must be Response objects.
    If the response received from Tryton is not a valid response then 
    this callable wraps the response with default mime_types
    """
    def __init__(self, endpoint):
        """
        Calls Tryton through Proteus and sets the model
        in the class. This model and method will be later
        called with the args later
        
        @param endpoint:Endpoint returned by the match of URL 
        """
        self.endpoint = endpoint
        #The endpoint is recorded in the format
        #`nereid.url/redirect`
        #`<model>/<callable>`
        model, method = endpoint.split('/')
        self.method = method
        #Try to get the tryton model
        #This error can be raised only when a custom
        #model is specified in the URL
        try:
            self.model_object = Model.get(str(model))
        except KeyError:
            raise ModelNotFound(model)
    
    def __call__(self, request, values):
        """
        Calls the method in Tryton for a response
        If the type of the response is not werkzeug.Response
        Then make it a response and send
        
        @param request: The Request Object 
        @param values: Values as a dictionary from the parsing of URL
        """
        logging.debug("Calling tryton for %s.%s with request %s/%s" % (
                                                        self.model_object,
                                                        self.method,
                                                        request,
                                                        values
                                                        )
        )
        #Call the method in Tryton object
        #TODO: Do a try catch?
        response_4m_tryton = getattr(
                                    self.model_object,
                                    self.method
                                    ).__call__(request, values)
        logging.debug("Response from Tryton:%s" % response_4m_tryton)
        #If the returned value is not an instance of werkzeug 
        #response
        if not isinstance(response_4m_tryton, BaseResponse):
            #Convert the reponse into a WSGI reponse
            return Response(response_4m_tryton)
        return response_4m_tryton


